#include "stdafx.h"
#include "Win32Exception.h"
#include "MD5HashProvider.h"

using namespace std;
using namespace ISAPI_Python::Win32Helpers;

MD5HashProvider::MD5HashProvider() :
  phCryptProv( NULL ),
  hHash( NULL ) {
	if(!CryptAcquireContext(&phCryptProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) {
		throw Win32Exception();
	}
	
	if(!CryptCreateHash(phCryptProv, CALG_MD5, 0, 0, &hHash)) {
		DWORD errorCode = GetLastError();

		CryptReleaseContext(phCryptProv, 0);
		
		throw Win32Exception(errorCode);
	}	
}

MD5HashProvider::MD5HashProvider(const MD5HashProvider& otherMD5HashProvider) throw() :
  phCryptProv ( otherMD5HashProvider.phCryptProv ),
  hHash ( otherMD5HashProvider.hHash ) {
}

MD5HashProvider& MD5HashProvider::operator =(const MD5HashProvider& otherMD5HashProvider) throw() {
	phCryptProv = otherMD5HashProvider.phCryptProv;
	hHash = otherMD5HashProvider.hHash;

	return *this; 
}

MD5HashProvider::~MD5HashProvider() throw() {
	CryptDestroyHash(hHash);
	CryptReleaseContext(phCryptProv, 0);
}

void MD5HashProvider::AddData(string value) {
	if(!CryptHashData(hHash, (LPBYTE)value.data(), (DWORD)value.length(), 0)) {
		throw Win32Exception();
	}
}

const string MD5HashProvider::GetHashString() const {
	DWORD hashSize;
	DWORD hashSizeLength = sizeof(hashSize);

	if(!CryptGetHashParam(hHash, HP_HASHSIZE, (LPBYTE)&hashSize, &hashSizeLength, 0)) {
		throw Win32Exception();
	}

	vector<BYTE> hashValue(hashSize);
	if(!CryptGetHashParam(hHash, HP_HASHVAL, &hashValue[0], &hashSize, 0)) {
		throw Win32Exception();
	}
	
	ostringstream returnValue;
	for(DWORD counter = 0; counter < hashSize; counter++) {
		returnValue << setw(2) << setfill('0') << hex << (int)hashValue[counter];
	}

	return returnValue.str();
}